program Win32Window;

{$APPTYPE CONSOLE}

{$R *.res}

uses
  System.SysUtils,
  Winapi.Windows,
  Winapi.Messages,
  VCL.Forms,
  SampleCommon,
  DIrr in '..\..\DSource\DIrr.pas',
  DIrr.Irr in '..\..\DSource\DIrr.Irr.pas',
  DIrr.GUI in '..\..\DSource\DIrr.GUI.pas',
  DIrr.Color in '..\..\DSource\DIrr.Color.pas',
  DIrr.Rect in '..\..\DSource\DIrr.Rect.pas',
  DIrr.Event in '..\..\DSource\DIrr.Event.pas',
  DIrr.Vector2D in '..\..\DSource\DIrr.Vector2D.pas',
  DIrr.Vector3D in '..\..\DSource\DIrr.Vector3D.pas',
  DIrr.Video in '..\..\DSource\DIrr.Video.pas',
  DIrr.Base in '..\..\DSource\DIrr.Base.pas',
  DIrr.Types in '..\..\DSource\DIrr.Types.pas',
  DIrr.Scene in '..\..\DSource\DIrr.Scene.pas',
  DIrr.Structs in '..\..\DSource\DIrr.Structs.pas',
  DIrr.Aabbox3D in '..\..\DSource\DIrr.Aabbox3D.pas',
  DIrr.Matrix4 in '..\..\DSource\DIrr.Matrix4.pas',
  DIrr.Texture in '..\..\DSource\DIrr.Texture.pas',
  DIrr.Collections in '..\..\DSource\DIrr.Collections.pas',
  DIrr.Consts in '..\..\DSource\DIrr.Consts.pas',
  DIrr.VCLApp in '..\..\DSource\DIrr.VCLApp.pas',
  VCLForm in 'VCLForm.pas' {FormVCL};

var
  WndHandle: HWND;
  OKButtonHandle: HWND;

  function CustomWndProc(Wnd: HWND; msg: UINT; ParamW: WPARAM; ParamL: LPARAM): LRESULT; stdcall;
  var
    hwndCtl: HWND;
    code: Integer;
  begin
    case msg of
      WM_COMMAND:
      begin
        hwndCtl := ParamL;
        code := ParamW;

        if hwndCtl = OKButtonHandle then
        begin
          DestroyWindow(hwndCtl);
          PostQuitMessage(0);
          Result := 0;
        end;
      end;

      WM_DESTROY:
      begin
        PostQuitMessage(0);
        Result := 0;
      end;

      else
        Result := DefWindowProc(Wnd, msg, ParamW, ParamL);
    end;
  end;

var
  Key: Char;
  DriverType: E_DRIVER_TYPE;
  Device: TIrrlichtDevice;
  Driver: TVideoDriver;

  Instance: HINST = 0;
  Win32ClassName: PChar = 'CIrrlichtWindowsTestDialog';
  wcex: TWndClassEx;
  Style: DWORD;
  WindowWidth: Integer = 440;
  WindowHeight: Integer = 380;

  ClientRect: TRect;
  IrrlichtWindowHandle: HWND;
  VideoData: SExposedVideoData;
  Param: SIrrlichtCreationParameters;
  Smgr: TSceneManager;
  DC: HDC;
  pfd: TPixelFormatDescriptor;
  pf: Integer;
  Cam: TCameraSceneNode;
  Anim: TSceneNodeAnimator;
  Cube: TSceneNode;
  EVDPtr: PExposedVideoData;

  Msg: TMsg;
begin
  try
    { TODO -oUser -cConsole Main : Insert code here }
    DriverType := DriverChoiceConsole;
    if DriverType = EDT_COUNT then
    begin
      ExitCode := 1;
      Exit;
    end;

    Writeln('Select the render window (some dead window may exist too):');
    Writeln(' (a) Window with button (via CreationParam)');
    Writeln(' (b) Window with button (via beginScene)');
    Writeln(' (c) VCL window (Delphi)');
    Writeln(' (d) Own Irrlicht window (default behavior)');

    Writeln(' (otherKey) exit');
    Writeln('');

    Readln(Key);
    if (Key <> 'a') and (Key <> 'b') and (Key <> 'c') and (Key <> 'd') then
    begin
      ExitCode := 1;
      Exit;
    end;

    if Key <> 'c' then   //not VCL Form
    begin
      FillChar(wcex, SizeOf(wcex), 0);
      wcex.cbSize := SizeOf(wcex);
      wcex.style := CS_HREDRAW or CS_VREDRAW;
      wcex.lpfnWndProc := @CustomWndProc;
      wcex.cbClsExtra := 0;
      wcex.cbWndExtra := DLGWINDOWEXTRA;
      wcex.hInstance := Instance;
      wcex.hIcon := 0;
      wcex.hCursor := LoadCursor(0, IDC_ARROW);
      wcex.hbrBackground := HBRUSH(COLOR_WINDOW);
      wcex.lpszMenuName := nil;
      wcex.lpszClassName := Win32ClassName;
      wcex.hIconSm := 0;

      RegisterClassEx(wcex);

      Style := WS_SYSMENU or WS_BORDER or WS_CAPTION or WS_CLIPCHILDREN or
        WS_CLIPSIBLINGS or WS_MAXIMIZEBOX or WS_MINIMIZEBOX or WS_SIZEBOX;

      WndHandle := CreateWindow(Win32ClassName, 'Irrlicht Win32 window example', Style,
        100, 100, WindowWidth, WindowHeight, 0, 0, Instance, nil);

      GetClientRect(WndHandle, ClientRect);
      WindowWidth := ClientRect.Right;
      WindowHeight := ClientRect.Bottom;

      // create ok button

      OKButtonHandle := CreateWindow('BUTTON', 'OK - Close', WS_CHILD or WS_VISIBLE or BS_TEXT,
        WindowWidth - 160, WindowHeight - 40, 150, 30, WndHandle, 0, Instance, nil);

      // create some text
      CreateWindow('STATIC', 'This is Irrlicht running inside a standard Win32 window.'#10 +
        'Also mixing with MFC and .NET Windows.Forms is possible.',
        WS_CHILD or WS_VISIBLE, 20, 20, 400, 40, WndHandle, 0, Instance, nil);

      // create window to put irrlicht in

      IrrlichtWindowHandle := CreateWindow('BUTTON', '', WS_CHILD or WS_VISIBLE or BS_OWNERDRAW,
        50, 80, 320, 220, WndHandle, 0, Instance, nil);

      if Key = 'b' then
        VideoData.Init(Pointer(IrrlichtWindowHandle))
      else
        VideoData.Init(nil);

      (*
      So now that we have some window, we can create an Irrlicht device
      inside of it. We use Irrlicht createEx() function for this. We only
      need the handle (HWND) to that window, set it as windowsID parameter
      and start up the engine as usual. That's it.
      *)
      // create irrlicht device in the button window
      Param.Init;
      Param.DriverType := DriverType;
      if Key = 'a' then
        Param.WindowId := Pointer(IrrlichtWindowHandle);

      Device := CreateDevice(Param);
      if not Assigned(Device) then
      begin
        ExitCode := 1;
        Exit;
      end;

      // setup a simple 3d scene
      Smgr := Device.GetSceneManager;
      Driver := Device.GetVideoDriver;

      if DriverType = EDT_OPENGL then
      begin
        DC := GetDC(IrrlichtWindowHandle);
        FillChar(pfd, SizeOf(pfd), 0);
        pfd.nSize := SizeOf(pfd);
        pf := GetPixelFormat(DC);
        DescribePixelFormat(DC, pf, SizeOf(pf), pfd);
        pfd.dwFlags := pfd.dwFlags or PFD_DOUBLEBUFFER or PFD_SUPPORT_OPENGL or PFD_DRAW_TO_WINDOW;
        pfd.cDepthBits := 16;
        pf := ChoosePixelFormat(DC, @pfd);
        SetPixelFormat(DC, pf, @pfd);
        VideoData.OpenGLWin32.HDC := Pointer(DC);
        VideoData.OpenGLWin32.HRC := Pointer(wglCreateContext(DC));
        EVDPtr := Driver.GetExposedVideoData;
        wglShareLists(HGLRC(EVDPtr^.OpenGLWin32.HRC), HGLRC(VideoData.OpenGLWin32.HRC));
      end;
    end
    else
    begin
      //VCL Form

      Application.Initialize;
      Application.CreateForm(TFormVCL, FormVCL);

      WndHandle := FormVCL.Handle;

      GetClientRect(WndHandle, ClientRect);
      WindowWidth := ClientRect.Right;
      WindowHeight := ClientRect.Bottom;

      Param.Init;
      Param.DriverType := DriverType;
      Param.WindowId := Pointer(FormVCL.Panel1.Handle);

      Device := CreateDevice(Param);
      if not Assigned(Device) then
      begin
        ExitCode := 1;
        Exit;
      end;

      Smgr := Device.GetSceneManager;
      Driver := Device.GetVideoDriver;

    end;

    Cam := Smgr.AddCameraSceneNode;
    Cam.SetTarget(Vector3DF(0, 0, 0));

    Anim := Smgr.CreateFlyCircleAnimator(Vector3DF(0, 15, 0), 30);
    Cam.AddAnimator(Anim);
    Anim.Drop;

    Cube := Smgr.AddCubeSceneNode(20);

    Cube.SetMaterialTexture(0, Driver.GetTexture('..\..\media\wall.bmp'));
    Cube.SetMaterialTexture(1, Driver.GetTexture('..\..\media\water.jpg'));
    Cube.SetMaterialFlag(EMF_LIGHTING, False);
    Cube.SetMaterialType(EMT_REFLECTION_2_LAYER);

    Smgr.AddSkyBoxSceneNode
    (
      Driver.GetTexture('..\..\media\irrlicht2_up.jpg'),
      Driver.GetTexture('..\..\media\irrlicht2_dn.jpg'),
      Driver.GetTexture('..\..\media\irrlicht2_lf.jpg'),
      Driver.GetTexture('..\..\media\irrlicht2_rt.jpg'),
      Driver.GetTexture('..\..\media\irrlicht2_ft.jpg'),
      Driver.GetTexture('..\..\media\irrlicht2_bk.jpg')
    );

    // show and execute dialog
    if Key <> 'c' then
    begin
      ShowWindow(WndHandle, SW_SHOW);
      UpdateWindow(WndHandle);
    end
    else
    begin
      FormVCL.Show;
    end;

    // do message queue

    (*
    Now the only thing missing is the drawing loop using
    IrrlichtDevice::run(). We do this as usual. But instead of this, there
    is another possibility: You can also simply use your own message loop
    using GetMessage, DispatchMessage and whatever. Calling
    Device->run() will cause Irrlicht to dispatch messages internally too.
    You need not call Device->run() if you want to do your own message
    dispatching loop, but Irrlicht will not be able to fetch user input
    then and you have to do it on your own using the window messages,
    DirectInput, or whatever.
    *)

    while (Device.Run and Assigned(Driver)) do
    begin
      Driver.BeginScene(True, True, IrrColor(0, 200, 200, 200));
      Smgr.DrawAll;
      Driver.EndScene;


      Application.DoProcMessage(Msg);
    end;

    (*
    The alternative, own message dispatching loop without Device->run()
    would look like this:
    *)

    (*MSG msg;
    while (true)
    {
      if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
      {
        TranslateMessage(&msg);
        DispatchMessage(&msg);

        if (msg.message == WM_QUIT)
          break;
      }

      // advance virtual time
      device->getTimer()->tick();

      // draw engine picture
      driver->beginScene(true, true, 0, (key=='c')?hIrrlichtWindow:0);
      smgr->drawAll();
      driver->endScene();
    }*)

    Device.CloseDevice;
    Device.Drop;
  except
    on E: Exception do
      Writeln(E.ClassName, ': ', E.Message);
  end;
end.
